package org.elwiki.editor;

import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;

import org.apache.log4j.Logger;
import org.eclipse.jface.window.Window;
import org.eclipse.rap.json.JsonObject;
import org.eclipse.rap.json.JsonValue;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.ClientFileLoader;
import org.eclipse.rap.rwt.remote.AbstractOperationHandler;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.rap.rwt.widgets.WidgetUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;

import erwiki.api.pages.PageManager;
//import org.elwiki.api.pagemanager.IPageManager;

public class PageEditor extends Composite {
	private static final long serialVersionUID = 3459998549125185474L;
	
	private static Logger log = Logger.getLogger(PageEditor.class);

	private final class InternalOperationHandler extends AbstractOperationHandler {
		private static final long serialVersionUID = 8355982464478859688L;

		private static final String METHOD_NEW_LINK = "newLink";
		private static final String PARAMETERS_LINK = "selection";

		@Override
		public void handleSet(JsonObject properties) {
			JsonValue textValue = properties.get("text");
			if (textValue != null) {
				setTextImpl(textValue.asString());
			}
		}

		@Override
		public void handleCall(String method, JsonObject parameters) {
			if (METHOD_NEW_LINK.equals(method)) {
				final String selection = parameters.get(PARAMETERS_LINK).asString();
				PageEditor.this.newLink(selection);
			}
		}

		@Override
		public void handleNotify(String event, JsonObject properties) {
			switch (event) {
			case "save":
				if (PageEditor.this.saveEventHandler != null) {
					PageEditor.this.saveEventHandler.actionPerformed(null);
				}
				break;
			case "cancel":
				if (PageEditor.this.cancelEventHandler != null) {
					PageEditor.this.cancelEventHandler.actionPerformed(null);
				}
				break;
			default:
			}
		}
	};

	private final String[] RESOURCE_FILES = {
			//@formatter:off
			"templates/haddock/fonts/FontJspwiki.eot",
			"templates/haddock/fonts/FontJspwiki.svg",
			"templates/haddock/fonts/FontJspwiki.ttf",
			"templates/haddock/fonts/FontJspwiki.woff",
			// -- -- -- -- -- -- -- -- -- -- -- -- --
			"templates/haddock/haddock.css",
			"i18.js",
			"templates/haddock/haddock.js",
			// "templates/haddock/haddock-edit.js",
		    "moo-extend/Behavior.js",
		    "moo-extend/Textarea.js",
			// ---------------------------------------
		    "dialogs/Dialog.js",
		    "dialogs/Dialog.Buttons.js",
		    "dialogs/Dialog.Color.js",
		    "dialogs/Dialog.Selection.js",
		    "dialogs/Dialog.Font.js",
		    "dialogs/Dialog.Chars.js",
		    "dialogs/Dialog.Find.js",
			// ---------------------------------------
			"eleditor.js",
			"PageTextEditor.js",
			"PageTextEditorHandler.js",
			"edit/undoable.js",
			"edit/pagelink.js",
			"edit/snipe.js",
			"edit/snipe.snips.js",
			"edit/snipe.commands.js",
			"edit/snipe.sections.js",
			"edit/wiki.Snips.js",
		    //@formatter:on
	};

	// The directory containing the file js, css. (реальный каталог проекта)
	private static final String RESOURCES_PATH = "resources/";

	private static final String REGISTER_PATH = "wpeditor/";

	private static final String REMOTE_TYPE = "elwiki.PageEditor";

	private RemoteObject remoteObject;

	private ActionListener cancelEventHandler;
	private ActionListener saveEventHandler;

	private String pageText;

	final PageManager pageManager;
	
	/**
	 * Create the composite.
	 *
	 * @param parent
	 * @param style
	 */
	public PageEditor(Composite parent, int style, PageManager pageManager) {
		super(parent, style);
		this.pageManager = pageManager;

		// Note: Catching error when viewed on WindowBuilder
		try {
			registerResources();
			registerFonts();
			loadWebFiles();

			getRemoteObject().setHandler(new InternalOperationHandler());
			getRemoteObject().set("parent", WidgetUtil.getId(this));
			getRemoteObject().listen("save", true);
			getRemoteObject().listen("cancel", true);
			getRemoteObject().listen("changed", true);
			// getRemoteObject().listen("newlink", true);

		} catch (Exception e) {
			e.printStackTrace();
			// throw new RuntimeException(e);
		}
	}

	protected void newLink(String selection) {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				PageLinkDialog dialog = new PageLinkDialog(getShell(), PageEditor.this.pageManager);
				dialog.create();
				dialog.setLinkText(selection);
				if (dialog.open() == Window.OK) {
					String linkText = dialog.getLinkText();
					String pageId = dialog.getPageId();
					setPagelink("[" + linkText + " | @" + pageId + "]");
				}
			}
		});
	}

	public void addSaveEventHandler(ActionListener actionListener) {
		this.saveEventHandler = actionListener;
	}

	public void addCancelEventHandler(ActionListener cancelEventHandler) {
		this.cancelEventHandler = cancelEventHandler;
	}

	private RemoteObject getRemoteObject() {
		if (this.remoteObject == null) {
			this.remoteObject = RWT.getUISession().getConnection().createRemoteObject(REMOTE_TYPE);
		}
		return this.remoteObject;
	}

	@Override
	public void dispose() {
		super.dispose();

		// Call destroy() function in rap-handler.js
		this.remoteObject.destroy(); // TODO: ... для JS.
	}

	/**
	 * Регистрация ресурсных файлов.
	 * 
	 * @throws IOException
	 */
	private void registerFonts() {
		ResourceManager resourceManager = RWT.getResourceManager();
		if (!resourceManager.isRegistered("foo/icon.png")) {

		}
	}

	/**
	 * Загрузка JS и CSS файлов клиентом. Файлы будут добавлены к клиентскому "head" элементу
	 * документов.
	 * 
	 * @throws IOException
	 */
	private void loadWebFiles() throws IOException {
		ClientFileLoader loader = RWT.getClient().getService(ClientFileLoader.class);
		ResourceManager resourceManager = RWT.getResourceManager();
		for (String fileName : this.RESOURCE_FILES) {
			switch (fileName.substring(fileName.lastIndexOf('.'))) {
			case ".js":
				loader.requireJs(resourceManager.getLocation(REGISTER_PATH + fileName));
				break;
			case ".css":
				loader.requireCss(resourceManager.getLocation(REGISTER_PATH + fileName));
				break;

			default:
				// :FVK: throw new IOException("Unknown resource file type: " + fileName);
			}
		}
	}

	private void registerResources() throws IOException {
		ResourceManager resourceManager = RWT.getResourceManager();

		// Check this resource has been registered yet. (WORKAROUND)
		boolean isRegistered = resourceManager.isRegistered(REGISTER_PATH + this.RESOURCE_FILES[0]);
		// :FVK: if (!isRegistered)
		{
			// Register "resources/abc.js"
			// as http://localhost:port/rwt-resources/logjs/abc.js
			ClassLoader classLoader = PageEditor.class.getClassLoader();
			try {
				for (String fileName : this.RESOURCE_FILES) {
					String resourcePath = RESOURCES_PATH + fileName;
					try (InputStream inputStream = classLoader.getResourceAsStream(resourcePath);) {
						if (inputStream == null) {
							throw new IOException("File not found " + resourcePath);
						}
						// Register resource
						resourceManager.register(REGISTER_PATH + fileName, inputStream);
						inputStream.close();
					}
				}
			} catch (IOException ioe) {
				throw new IllegalArgumentException("Failed to load resources. " + ioe.getMessage(), ioe);
			}
		}
	}

	@Override
	protected void checkSubclass() {
		//
	}

	protected void setTextImpl(String value) {
		this.pageText = value;
	}

	protected void setPagelink(String pageLink) {
		checkWidget();
		if (pageLink == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
		this.remoteObject.set("pagelink", pageLink);
	}

	public String getText() {
		checkWidget();
		return this.pageText;
	}

	public void setText(String text) {
		checkWidget();
		if (text == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
		this.pageText = text;
		this.remoteObject.set("text", text);
	}

}
